//
// Created by tcj on 2022/3/24.
//

#ifndef _SYLAR_SOCKET_H__
#define _SYLAR_SOCKET_H__

#include "memory"
#include "address.h"
#include "noncopyable.h"

namespace sylar {
    class Socket : public std::enable_shared_from_this<Socket>, Noncopyable {
    public:
        typedef std::shared_ptr<Socket> ptr;
        typedef std::weak_ptr<Socket> weak_ptr;
        enum Type {
            TCP = SOCK_STREAM,
            UDP = SOCK_DGRAM
        };

        enum Family {
            IPv4 = AF_INET,
            IPv6 = AF_INET6,
            UNIX = AF_UNIX
        };

        static Socket::ptr CreateTCP(sylar::Adress::ptr address);

        static Socket::ptr CreateUDP(sylar::Adress::ptr address);

        static Socket::ptr CreateTCPSocket();

        static Socket::ptr CreateUDPSocket();

        static Socket::ptr CreateTCPSocket6();

        static Socket::ptr CreateUDPSocket6();

        static Socket::ptr CreateUnixTCPSocket();

        static Socket::ptr CreateUnixUDPSocket();

        Socket(int family, int type, int protocol = 0);

        ~Socket();

        int64_t getSendTimeout();

        void setSendTimeout(int64_t v);

        int64_t getRecvTimeout();

        void setRecvTimeout(int64_t v);

        bool getOption(int level, int option, void *result, size_t *len);

        template<class T>
        bool getOption(int level, int option, T &result) {
            size_t length = sizeof(T);
            return getOption(level, &result, &length);
        }

        bool setOption(int level, int option, const void *result, size_t len);

        template<class T>
        bool setOption(int level, int option, const T &value) {
            return setOption(level, option, &value, sizeof(T));
        }

        Socket::ptr accept();


        bool bind(const Adress::ptr addr);

        bool connect(const Adress::ptr addr, uint64_t timeout_ms = -1);

        bool listen(int backlog = SOMAXCONN);

        bool close();

        int send(const void *buffer, size_t length, int flags = 0);

        int send(const iovec *buffers, size_t length, int flags = 0);

        int sendTo(const void *buffer, size_t length, const Adress::ptr to, int flags = 0);

        int sendTo(const iovec *buffers, size_t length, const Adress::ptr to, int flags = 0);

        int recv(void *buffer, size_t length, int flags = 0);

        int recv(iovec *buffers, size_t length, int flags = 0);

        int recvFrom(void *buffer, size_t length, Adress::ptr from, int flags = 0);

        int recvFrom(iovec *buffers, size_t length, Adress::ptr from, int flags = 0);

        Adress::ptr getRemoteAddress();

        Adress::ptr getLocalAddress();

        int getFamily() const { return m_family; }

        int getType() const { return m_type; }

        int getProtocol() const { return m_protocol; }

        bool isConnected() const { return m_isConnected; }

        bool isVaild() const;

        int getError();

        std::ostream &dump(std::ostream &os) const;

        int getSocket() const { return m_sock; }

        bool cancelRead();

        bool cancelWrite();

        bool cancelAccept();

        bool cancelAll();

    private:
        void initSock();

        void newSock();

        bool init(int sock);

    private:
        int m_sock;
        int m_family;
        int m_type;
        int m_protocol;
        bool m_isConnected;

        Adress::ptr m_localAddress;
        Adress::ptr m_remoteAddress;
    };

    std::ostream &operator<<(std::ostream &os, const Socket &sock);
}


#endif //SERVER_FRAMEWORK_SOCKET_H
